package com.bkgy.modularbx.slip.service.imp;

import com.bkgy.modularbx.slip.entity.RollData;
import com.bkgy.modularbx.slip.entity.RollMapping;
import com.bkgy.modularbx.slip.entity.RollMappingConfiguration;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * 轧辊数据采集服务类，负责从外部API获取轧辊运行数据并进行处理。
 * 该服务定期从指定的API端点获取实时数据，包括速度、扭矩和张力等参数，
 * 并将处理后的数据存储到数据缓冲区中，为打滑检测分析提供数据支持。
 *
 * @author Trae
 * @version 1.0
 * @see com.bkgy.modularbx.slip.entity.RollData
 * @see com.bkgy.modularbx.slip.entity.RollMapping
 * @see com.bkgy.modularbx.slip.entity.RollMappingConfiguration
 * @see DataBufferService
 */
@Slf4j
@Service
public class DataCollectionService {

    /** API端点URL，用于获取实时数据 */
    @Value("${api.endpoint.url:http://10.171.7.100:8090/restapi}")
    private String apiEndpointUrl;

    /** 生产线名称，用于标识数据来源 */
    @Value("${api.endpoint.lineName:CGL1870_RealData}")
    private String lineName;

    /** REST请求客户端 */
    @Resource
    private RestTemplate restTemplate;
    
    /** JSON数据处理工具 */
    private final ObjectMapper objectMapper;
    
    /** 轧辊映射配置，包含数据采集参数设置 */
    private final RollMappingConfiguration config;
    
    /** 数据缓冲服务，用于存储采集的数据 */
    private final DataBufferService dataBufferService;

    @Autowired
    public DataCollectionService(
            RestTemplate restTemplate,
            ObjectMapper objectMapper,
            RollMappingConfiguration config,
            DataBufferService dataBufferService) {
        this.restTemplate = restTemplate;
        this.objectMapper = objectMapper;
        this.config = config;
        this.dataBufferService = dataBufferService;
    }

    /**
     * 执行数据采集任务。
     * 该方法通过HTTP请求从API获取实时数据，解析响应数据并提取所需的参数值。
     * 对于每个配置的轧辊，都会创建对应的数据对象，设置速度、扭矩和张力等参数，
     * 并根据区域设置对应的主速度参考值。只有在数据有效的情况下才会将数据存入缓冲区。
     */
    //@Scheduled(fixedDelayString = "${slip.detection.sampling.interval:500}")
    public void collectData() {
        try {
            // 从API获取数据
            String url = apiEndpointUrl + "?name=" + lineName;
            String response = restTemplate.getForObject(url, String.class);

            if (response == null || response.trim().isEmpty()) {
                log.warn("Empty response from API");
                return;
            }

            // 解析JSON响应
            Map<String, Object> apiData = objectMapper.readValue(response, HashMap.class);

            // 获取全局主速度参考
            Map<String, String> speedRefParams = config.getSpeedReferenceParams();
            float entrySpeed = getFloatValue(apiData, speedRefParams.get("entrySpeed"));
            float processSpeed = getFloatValue(apiData, speedRefParams.get("processSpeed"));
            float exitSpeed = getFloatValue(apiData, speedRefParams.get("exitSpeed"));

            // 处理每个监控的轧辊
            LocalDateTime now = LocalDateTime.now();
            for (RollMapping rollMapping : config.getRollMappings()) {
                String rollId = rollMapping.getRollId();

                RollData rollData = new RollData();
                rollData.setRollId(rollId);
                rollData.setDisplayName(rollMapping.getDisplayName());
                rollData.setSection(rollMapping.getSection());
                rollData.setPosition(rollMapping.getPosition());
                rollData.setTimestamp(now);

                Map<String, String> params = rollMapping.getParams();

                // 设置速度、扭矩和张力数据
                if (params.get("speedSet") != null) {
                    rollData.setSpeedSet(getFloatValue(apiData, params.get("speedSet")));
                } else {
                    rollData.setSpeedSet(Float.NaN);
                }

                if (params.get("speedAct") != null) {
                    rollData.setSpeedAct(getFloatValue(apiData, params.get("speedAct")));
                } else {
                    rollData.setSpeedAct(Float.NaN);
                }

                if (params.get("torqueAct") != null) {
                    rollData.setTorqueAct(getFloatValue(apiData, params.get("torqueAct")));
                } else {
                    rollData.setTorqueAct(Float.NaN);
                }

                if (params.get("tensionAct") != null) {
                    rollData.setTensionAct(getFloatValue(apiData, params.get("tensionAct")));
                } else {
                    rollData.setTensionAct(Float.NaN);
                }

                // 根据区域设置主速度参考
                String section = rollMapping.getSection();
                if (section.contains("入口") || section.equals("清洗段") || section.equals("炉前段")) {
                    rollData.setMasterSpeed(entrySpeed);
                } else if (section.contains("出口") || section.equals("拉矫段")) {
                    rollData.setMasterSpeed(exitSpeed);
                } else {
                    rollData.setMasterSpeed(processSpeed);
                }

                // 只有当数据有效时才添加到缓冲区
                if (rollData.isValidForSlipAnalysis()) {
                    dataBufferService.addDataPoint(rollId, rollData);
                }
            }
        } catch (RestClientException e) {
            log.error("Error connecting to API: {}", e.getMessage());
        } catch (JsonProcessingException e) {
            log.error("Error parsing API response: {}", e.getMessage());
        } catch (Exception e) {
            log.error("Unexpected error during data collection: {}", e.getMessage(), e);
        }
    }

    /**
     * 从数据映射中安全地获取浮点值。
     * 该方法处理各种可能的数据格式和异常情况，确保返回有效的浮点数值。
     * 当无法获取或转换值时，返回Float.NaN。
     *
     * @param data 包含键值对的数据映射
     * @param key 要获取值的键名
     * @return 转换后的浮点值，如果无法获取或转换则返回Float.NaN
     */
    private float getFloatValue(Map<String, Object> data, String key) {
        if (key == null || !data.containsKey(key)) {
            return Float.NaN;
        }

        Object value = data.get(key);
        if (value == null) {
            return Float.NaN;
        }

        try {
            if (value instanceof Number) {
                return ((Number) value).floatValue();
            } else if (value instanceof String) {
                return Float.parseFloat((String) value);
            }
        } catch (NumberFormatException e) {
            log.warn("Failed to parse value for key {}: {}", key, value);
        }

        return Float.NaN;
    }
}